#!/usr/bin/env python
"""
:mod:`pynchon.bio.data.alphabet`
================================

Provides biological alphabets.
"""
import numpy as np

# Common
IUPAC_GAP = ('-',)
IUPAC_MISSING = ('?',)

# Amino Acid
IUPAC_PROTEIN_STANDARD = ('A','C','D','E','F','G','H','I','K','L',
                          'M','N','P','Q','R','S','T','V','W','Y')
IUPAC_PROTEIN_NONSTANDARD = ('O', 'U')
IUPAC_PROTEIN_AMBIGUOUS = ('B','J','X','Z')
IUPAC_PROTEIN_STOP = ('*',)
IUPAC_PROTEIN_ALPHABET = IUPAC_PROTEIN_STANDARD + IUPAC_PROTEIN_NONSTANDARD + \
                         IUPAC_PROTEIN_AMBIGUOUS + IUPAC_PROTEIN_STOP + \
                         IUPAC_GAP + IUPAC_MISSING

# DNA
IUPAC_DNA = ('T','C','A','G')
IUPAC_DNA_AMBIGUOUS = ('B','D','H','K','M','N','R','S','V','W','Y')
IUPAC_DNA_ALPHABET = IUPAC_DNA + IUPAC_DNA_AMBIGUOUS + IUPAC_GAP + IUPAC_MISSING
IUPAC_DNA_CODONS = tuple([(i,j,k) for i in IUPAC_DNA for j in IUPAC_DNA for k in IUPAC_DNA])
# RNA
IUPAC_RNA = ('U','C','A','G')
IUPAC_RNA_AMBIGUOUS = ('B','D','H','K','M','N','R','S','V','W','Y')                         
IUPAC_RNA_ALPHABET = IUPAC_RNA + IUPAC_RNA_AMBIGUOUS + IUPAC_GAP + IUPAC_MISSING
IUPAC_RNA_CODONS = tuple([(i,j,k) for i in IUPAC_RNA for j in IUPAC_RNA for k in IUPAC_RNA])
# NA
IUPAC_NA = tuple(sorted(set((IUPAC_DNA, IUPAC_RNA))))
IUPAC_NA_AMBIGUOUS = tuple(sorted(set((IUPAC_DNA_AMBIGUOUS, IUPAC_RNA_AMBIGUOUS))))
IUPAC_NA_ALPHABET = tuple(sorted(set(IUPAC_DNA_ALPHABET + IUPAC_RNA_ALPHABET)))
IUPAC_NA_CODONS = tuple(sorted(set(IUPAC_DNA_CODONS + IUPAC_RNA_CODONS)))




# joint
IUPAC_ALPHABET = tuple(sorted(set(IUPAC_NA_ALPHABET + IUPAC_PROTEIN_ALPHABET)))
IUPAC_WORDS = tuple(sorted(set(IUPAC_ALPHABET + IUPAC_NA_CODONS)))

# Mappings
IUPAC_PROTEIN_THREE = \
    {
    'A':'Ala', 
    'B':'Asx', 
    'C':'Cys', 
    'D':'Asp',
    'E':'Glu', 
    'F':'Phe', 
    'G':'Gly', 
    'H':'His',
    'I':'Ile',
    'J':'Xle',
    'K':'Lys', 
    'L':'Leu', 
    'M':'Met',
    'N':'Asn',
    'O':'Pyl',
    'P':'Pro', 
    'Q':'Gln', 
    'R':'Arg',
    'S':'Ser', 
    'T':'Thr',
    'U':'Sec',     
    'V':'Val', 
    'W':'Trp',
    'X':'Xaa',    
    'Y':'Tyr', 
    'Z':'Glx'}
        
IUPAC_PROTEIN_TO_STANDARD_WITH_GAP = \
    {
    'A':'A', 
    'B':'D', # D > N
    'C':'C', 
    'D':'D',
    'E':'E', 
    'F':'F', 
    'G':'G', 
    'H':'H',
    'I':'I',
    'J':'L', # L > I
    'K':'K', 
    'L':'L', 
    'M':'M',
    'N':'N',
    'O':'K',
    'P':'P', 
    'Q':'Q', 
    'R':'R',
    'S':'S', 
    'T':'T',
    'U':'C',     
    'V':'V', 
    'W':'W',
    'X':IUPAC_GAP[0], #
    'Y':'Y', 
    'Z':'Q', # Q > E
    IUPAC_GAP[0]:IUPAC_GAP[0],    
    IUPAC_MISSING[0]:IUPAC_GAP[0],
    IUPAC_PROTEIN_STOP[0]:IUPAC_GAP[0]    
    }

IUPAC_PROTEIN_ONE = \
    {
    'Ala':'A',
    'Arg':'R',
    'Asn':'N',
    'Asp':'D',
    'Asx':'B',
    'Cys':'C',
    'Gln':'Q',
    'Glu':'E',
    'Glx':'Z',
    'Gly':'G',
    'His':'H',
    'Ile':'I',
    'Leu':'L',
    'Lys':'K',
    'Met':'M',
    'Mse':'M',
    'Phe':'F',
    'Pro':'P',
    'Pyl':'O',
    'Sec':'U',
    'Ser':'S',
    'Thr':'T',
    'Trp':'W',
    'Tyr':'Y',
    'Val':'V',
    'Xaa':'X',
    'Xle':'J'}

IUPAC_PROTEIN_NAMES = \
    {
    '*':'translation stop',
    '-':'gap',
    '?':'missing',
    'A':'alanine',
    'B':'aspartic acid or asparagine',
    'C':'cysteine',
    'D':'aspartic acid',
    'E':'glutamic acid',
    'F':'phenylalanine',
    'G':'glycine',
    'H':'histidine',
    'I':'isoleucine',
    'J':'leucine or isoleucine',
    'K':'lysine',
    'L':'leucine',
    'M':'methionine',
    'N':'asparagine',
    'O':'pyrrolysine',
    'P':'proline',
    'Q':'glutamine',
    'R':'arginine',
    'S':'serine',
    'T':'threonine',
    'U':'selenocysteine',
    'V':'valine',
    'W':'tryptophan',
    'X':'unknown',
    'Y':'tyrosine',
    'Z':'glutamic acid or glutamine',
    }

IUPAC_NA_NAMES = \
    { 
    'A':'Adenosine',
    'C':'Cytidine',
    'G':'Guanine',
    'T':'Thymidine',
    'U':'Uracil',
    'R':'G A (puRine)',
    'Y':'T C (pYrimidine)',
    'K':'G T (Ketone)',
    'M':'A C (aMino group)',
    'S':'G C (Strong interaction)',
    'W':'A T (Weak interaction)',
    'B':'G T C (not A) (B comes after A)',
    'D':'G A T (not C) (D comes after C)',
    'H':'A C T (not G) (H comes after G)',
    'V':'G C A (not T, not U) (V comes after U)',
    'N':'A G C T (aNy)',
    '-':'gap', 
    }

IUPAC_DNA_ALPHABET_TO_IUPAC_DNA = \
    {
    'T':('T',),
    'C':('C',),
    'A':('A',),
    'G':('G',),
    'N':('T','C', 'A', 'G'),
    'R':('A','G'),
    'Y':('T','C'),
    'W':('T','A'),
    'S':('C','G'),
    'K':('T','G'),
    'M':('C','A'),
    'B':('T','C','G'),
    'D':('T','A','G'),
    'H':('T','C','A'),
    'V':('C','A','G')
    }  

IUPAC_DNA_TO_IUPAC_DNA_ALPHABET = \
    {
    'A':('A','D','H','M','R','V','W'),
    'C':('C','B','H','M','S','V','Y'),
    'G':('G','B','D','K','R','S','V'),
    'T':('T','B','D','H','K','Y','W'),
    }

IUPAC_RNA_ALPHABET_TO_IUPAC_DNA = \
    {
    'U':('U',),
    'C':('C',),
    'A':('A',),
    'G':('G',),
    'N':('U','C', 'A', 'G'),
    'R':('A','G'),
    'Y':('U','C'),
    'W':('U','A'),
    'S':('C','G'),
    'K':('U','G'),
    'M':('C','A'),
    'B':('U','C','G'),
    'D':('U','A','G'),
    'H':('U','C','A'),
    'V':('C','A','G')
    }  

IUPAC_RNA_TO_IUPAC_RNA_ALPHABET = \
    {
    'A':('A','D','H','M','R','V','W'),
    'C':('C','B','H','M','S','V','Y'),
    'G':('G','B','D','K','R','S','V'),
    'U':('U','B','D','H','K','Y','W'),
    }

class _Proxy(dict):
    """
    A proxy, which maps elements (e.g. symbols or codons) to rank-0 
    ``numpy.ndarrays``. e.g. ``'A'`` becomes ``array('A', dtype='|S1')``
    the purpose of doing that is performance and structured element support 
    (e.g. ``('A','G','T')`` in comparisons and to not include arrays in
    ``pynchon.core.Entity.metadata``.
    
    Arguments:
    
        - elements (sequence(s) of elements) Possibly multiple sequences of 
          elements e.g. ``IUPAC_PROTEIN_STANDARD``.
    """
    
    def _add_element(self, element):
        s1u1 = 'S1' if isinstance(element[0], str) else 'U1'
        dt = [('i%s' % i, s1u1) for i in xrange(len(element))] \
                if isinstance(element, tuple) else \
             s1u1
        self[element] = np.array(element, dtype=dt)
    
    def __init__(self, *elements):
        for els in elements:
            for element in els:
                self._add_element(element)
        dict.__init__(self)
        
    def __getitem__(self, k):
        array = super(_Proxy, self).get(k)
        if array is None:
            self._add_element(k)
            return self[k]
        else:
            return array
        

NUMPY_PROXY = _Proxy(IUPAC_WORDS)

    
                
             




